﻿// Copyright (C) Jari Hautio <jari.hautio@iki.fi> 2008. Licensed under GPLv2. See LICENSE.txt in solution folder.
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace RyijyApp
{
    public class SymbolMap
    {
        interface ISymbolItem 
        {
            void Draw(Graphics g, RectangleF to);
        }
 
        delegate void DrawDelegate(Graphics g, RectangleF to);
        // symbol, which is draw using a delegate function
        class DelegateSymbolItem : ISymbolItem
        {
           public DrawDelegate OnDraw;

                
        #region ISymbolItem Members

        public void  Draw(Graphics g, RectangleF to)
        {
            if (OnDraw != null)
                OnDraw(g, to);
        }

        #endregion
        }
        /// <summary>
        /// Symbol using polygon point array to draw the symbol
        /// </summary>
        class PolygonSymbol : ISymbolItem
        {
            PointF[] pts = null;
            public enum Mode { Draw, Fill, Alternate };
            Mode mode = Mode.Draw;
            public PolygonSymbol(PointF[] points, Mode m)
            {
                pts = (PointF[])points.Clone();
                mode = m;
            }
            public PolygonSymbol(Mode m, float[] points)
            {
                pts = new PointF[points.Length / 2];
                for (int i = 1; i < points.Length; i += 2)
                {
                    pts[i / 2] = new PointF(points[i - 1], points[i]); 
                }
                mode = m;
            }

            public void Draw(Graphics g, RectangleF to)
            {
                PointF[] newPoints = new PointF[pts.Length];
                for(int i = 0;i<pts.Length;i++)
                {
                    newPoints[i] = new PointF(to.X + pts[i].X * to.Width, 
                                              to.Y + pts[i].Y * to.Height);
                }
                if (mode == Mode.Draw)
                {
                    g.DrawPolygon(Pens.Black, newPoints);
                }
                else
                {
                    g.FillPolygon(Brushes.Black, newPoints, 
                        (mode == Mode.Fill) ? System.Drawing.Drawing2D.FillMode.Winding :
                        System.Drawing.Drawing2D.FillMode.Alternate);
                }
            }
        }
        /// <summary>
        /// Symbol using line strip point array to draw the symbol
        /// </summary>
        class LinesSymbol : ISymbolItem
        {
            float[] pts = null;
            public LinesSymbol(float[] points)
            {
                pts = (float[])points.Clone();
            }

            public void Draw(Graphics g, RectangleF to)
            {
                PointF[] newPoints = new PointF[pts.Length/2];
                for(int i = 0;i<newPoints.Length;i++)
                {
                    newPoints[i] = new PointF(to.X + pts[i*2]   * to.Width, 
                                              to.Y + pts[i*2+1] * to.Height);
                }
                g.DrawLines(Pens.Black, newPoints);
            }
        }

        /// <summary>
        /// Symbol using individual line point array to draw the symbol
        /// </summary>
        class LineSymbol : ISymbolItem
        {
            float[] pts = null;
            public LineSymbol(float[] points)
            {
                pts = (float[])points.Clone();
            }

            public void Draw(Graphics g, RectangleF to)
            {
                for (int i = 3; i < pts.Length; i+=4)
                {
                    g.DrawLine(Pens.Black, 
                        to.X + pts[i-3] * to.Width,
                        to.Y + pts[i-2] * to.Height, 
                        to.X + pts[i-1] * to.Width,
                        to.Y + pts[i-0] * to.Height);
                }
            }
        }
        class DummySymbol : ISymbolItem
        {
            
            static Font[] fonts1 = new Font[10] { 
                new Font(FontFamily.GenericMonospace, 1,FontStyle.Bold),
                new Font(FontFamily.GenericMonospace, 1.5F,FontStyle.Bold),
                new Font(FontFamily.GenericMonospace, 2,FontStyle.Bold),
                new Font(FontFamily.GenericMonospace, 3,FontStyle.Bold),
                new Font(FontFamily.GenericMonospace, 4,FontStyle.Bold),
                new Font(FontFamily.GenericMonospace, 6,FontStyle.Bold), 
                new Font(FontFamily.GenericMonospace, 8,FontStyle.Bold), 
                new Font(FontFamily.GenericMonospace, 12,FontStyle.Bold),
                new Font(FontFamily.GenericMonospace, 16,FontStyle.Bold),
                new Font(FontFamily.GenericMonospace, 24,FontStyle.Bold) };
            
            
            static Font[] fonts2 = new Font[10] { 
                new Font("Symbol", 1,FontStyle.Bold),
                new Font("Symbol", 1.5F,FontStyle.Bold),
                new Font("Symbol", 2,FontStyle.Bold),
                new Font("Symbol", 3,FontStyle.Bold),
                new Font("Symbol", 4,FontStyle.Bold),
                new Font("Symbol", 6,FontStyle.Bold), 
                new Font("Symbol", 8,FontStyle.Bold), 
                new Font("Symbol", 12,FontStyle.Bold),
                new Font("Symbol", 16,FontStyle.Bold),
                new Font("Symbol", 24,FontStyle.Bold) };
             
            //            static Font[] fonts = new Font[4] { new Font("Arial", 4), new Font("Arial", 8), new Font("Arial", 12), new Font("Arial", 24) };
            string ch = "X";
            public enum SymbolPool { Alphanum, Symbol };
            SymbolPool symbolPool = SymbolPool.Alphanum;
            #region ISymbolItem Members
            public DummySymbol() { }
            public DummySymbol(string symbol, SymbolPool pool)
            {
                ch = symbol;
                symbolPool = pool;
            }

            public void Draw(Graphics g, RectangleF to)
            {
                Font[] fonts = symbolPool == SymbolPool.Alphanum ? fonts1 : fonts2;
                Font f = fonts[0];
                for (int i = 0; i < fonts.Length; i++)
                {
                    SizeF fs = g.MeasureString("x", fonts[i]);
                    if (fs.Height < (to.Height*1.2))
                        f = fonts[i];
                }
                StringFormat sf = new StringFormat();
                sf.Alignment = StringAlignment.Center;
                sf.FormatFlags = StringFormatFlags.FitBlackBox;
                g.DrawString(ch, f, Brushes.Black,
                    to,
                    sf);
            }

            #endregion
        }
        List<ISymbolItem> freeSymbols = new List<ISymbolItem>();
        Dictionary<Color, ISymbolItem> symbols = new Dictionary<Color, ISymbolItem>();
        Dictionary<Color, Brush> symbolColors = new Dictionary<Color, Brush>();
        int nextDummy = Convert.ToInt32('A');

        public SymbolMap()
        {
            CreateSymbols();
        }
        ISymbolItem GetSymbol(Color c)
        {
            ISymbolItem item;
            if (!symbols.TryGetValue(c, out item))
            {
                if (freeSymbols.Count > 0)
                {
                    item = freeSymbols[0];
                    freeSymbols.RemoveAt(0);
                }
                else
                {
                    if (nextDummy < 255)
                    {
                        item = new DummySymbol(Convert.ToChar(nextDummy).ToString(), DummySymbol.SymbolPool.Alphanum);
                    }
                    else if (nextDummy < 2*255)
                    {
                        item = new DummySymbol(Convert.ToChar(nextDummy-255).ToString(), DummySymbol.SymbolPool.Symbol);
                    }
                    else item = new DummySymbol(" ", DummySymbol.SymbolPool.Alphanum);

                    nextDummy++;
                    if (nextDummy == 127) nextDummy = 128;
                    else if (nextDummy == 254) nextDummy = 255 + 33;
                    else if (nextDummy == 255+125) nextDummy = 255+161;
                }
                symbols.Add(c, item);
            }
            return item;
        }
        Brush GetSymbolBrush(ISymbolItem item, Color c)
        {
            Brush br;
            if (!symbolColors.TryGetValue(c, out br))
            {
                System.Drawing.Drawing2D.HatchStyle[] hatches = new System.Drawing.Drawing2D.HatchStyle[8] {
                    System.Drawing.Drawing2D.HatchStyle.Percent70,
                    System.Drawing.Drawing2D.HatchStyle.Percent60,
                    System.Drawing.Drawing2D.HatchStyle.Percent50,
                    System.Drawing.Drawing2D.HatchStyle.Percent40,
                    System.Drawing.Drawing2D.HatchStyle.Percent30,
                    System.Drawing.Drawing2D.HatchStyle.Percent20,
                    System.Drawing.Drawing2D.HatchStyle.Percent10,
                    System.Drawing.Drawing2D.HatchStyle.Percent05,
                };
                Bitmap bm = new Bitmap(4, 4, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                using (Graphics gr2 = Graphics.FromImage(bm))
                {
                    gr2.FillRectangle(Brushes.White, 0, 0, bm.Width, bm.Height);
                    item.Draw(gr2, new RectangleF(0, 0, bm.Width, bm.Height));
                }
                float r = 0;
                for (int i = 0; i < bm.Width * bm.Height; i++)
                {
                    r += bm.GetPixel(i % bm.Width, i / bm.Width).R;
                }
                r /= (bm.Width * bm.Height);
                bm.Dispose();

                int lvl = Convert.ToInt32(r) / 32;
                if (lvl > 7) lvl = 7;
                br = new System.Drawing.Drawing2D.HatchBrush(hatches[lvl], Color.Black, Color.Transparent);
                symbolColors.Add(c, br);
            }
            return br;
        }
        /// <summary>
        /// Draws a symbol for given color
        /// </summary>
        /// <param name="g"></param>
        /// <param name="to"></param>
        /// <param name="c"></param>
        public void DrawSymbol(Graphics gr, RectangleF to, Color c)
        {
            ISymbolItem item = GetSymbol(c);

            // only draw if width is large enough, then draw bw box
            if (to.Width < 4)
            {
                Brush br = GetSymbolBrush(item, c);
                gr.FillRectangle(br, to);
            }
            else
            {

                to.Inflate(-to.Width / 10.0F, -to.Height / 10.0F);
                item.Draw(gr, to);
            }
        }
        /// <summary>
        /// Add new color and assign a new symbol for it
        /// </summary>
        /// <param name="c"></param>
        public void AddColor(Color c)
        {
        }
        void CreateSymbols()
        {
            DelegateSymbolItem s;
            s = new DelegateSymbolItem();
            s.OnDraw = delegate(Graphics g, RectangleF to)
            {
                to.Inflate(-to.Width * 0.3F, -to.Height * 0.3F);
                g.FillRectangle(Brushes.Black, to);
            };
            freeSymbols.Add(s);
            s = new DelegateSymbolItem();
            s.OnDraw = delegate(Graphics g, RectangleF to)
            {
                g.DrawEllipse(Pens.Black, to);
            };
            freeSymbols.Add(s);
            s = new DelegateSymbolItem();
            s.OnDraw = delegate(Graphics g, RectangleF to)
            {
                g.DrawEllipse(Pens.Black, to);
                to.Inflate(-to.Width * 0.3F, -to.Height * 0.3F);
                g.FillEllipse(Brushes.Black, to);
            };
            freeSymbols.Add(s);

            // U etc
            s = new DelegateSymbolItem();
            s.OnDraw = delegate(Graphics g, RectangleF to)
            {
                to.Inflate(-to.Width * 0.2F, -to.Height * 0.1F);
                g.DrawBezier(Pens.Black, to.Left, to.Top, to.Left, to.Bottom,
                                to.Right, to.Bottom, to.Right, to.Top);
            };
            freeSymbols.Add(s);

            // circle and cross
            s = new DelegateSymbolItem();
            s.OnDraw = delegate(Graphics g, RectangleF to)
            {
                g.DrawEllipse(Pens.Black, to);
                to.Inflate(-to.Width * 0.1F, -to.Height * 0.1F);
                g.DrawLine(Pens.Black, to.Left, to.Top, to.Right, to.Bottom);
                g.DrawLine(Pens.Black, to.Left, to.Bottom, to.Right, to.Top);
            };
            freeSymbols.Add(s);

            s = new DelegateSymbolItem();
            s.OnDraw = delegate(Graphics g, RectangleF to)
            {
                to.Inflate(-to.Width * 0.1F, -to.Height * 0.1F);
                g.FillEllipse(Brushes.Black, to);
            };
            freeSymbols.Add(s);
            s = new DelegateSymbolItem();
            s.OnDraw = delegate(Graphics g, RectangleF to)
            {
                g.DrawPie(Pens.Black, to, 0, 180);
            };
            freeSymbols.Add(s);
            s = new DelegateSymbolItem();
            s.OnDraw = delegate(Graphics g, RectangleF to)
            {
                g.FillPie(Brushes.Black, to.X, to.Y, to.Width,to.Height, 0, -180);
            };
            freeSymbols.Add(s);

            freeSymbols.Add(new LineSymbol(new float[] { 0.5F, 0, 0.5F, 1, 0, 0.5F, 1, 0.5F }));

            s = new DelegateSymbolItem();
            s.OnDraw = delegate(Graphics g, RectangleF to)
            {
                g.DrawRectangle(Pens.Black, to.X, to.Y, to.Width, to.Height);
            };
            freeSymbols.Add(s);

            s = new DelegateSymbolItem();
            s.OnDraw = delegate(Graphics g, RectangleF to)
            {
                to.Inflate(-to.Width * 0.1F, -to.Height * 0.1F);
                g.FillRectangle(Brushes.Black, to);
            };
            freeSymbols.Add(s);

            s = new DelegateSymbolItem();
            s.OnDraw = delegate(Graphics g, RectangleF to)
            {
                to.Inflate(-to.Width * 0.1F, -to.Height * 0.1F);
                g.DrawRectangle(Pens.Black, to.Left, to.Top, to.Width, to.Height);
                g.DrawLine(Pens.Black, to.Left, to.Top, to.Right, to.Bottom);
            };
            freeSymbols.Add(s);
            
            freeSymbols.Add(new PolygonSymbol(new PointF[] 
                              { new PointF(0.5F, 0), 
                                new PointF(1, 0.5F),
                                new PointF(0.5F, 1),
                                new PointF(0, 0.5F)
                              }, PolygonSymbol.Mode.Draw));
            freeSymbols.Add(new PolygonSymbol(new PointF[] 
                              { new PointF(0.5F, 0), 
                                new PointF(1, 0.5F),
                                new PointF(0.5F, 1),
                                new PointF(0, 0.5F)
                              }, PolygonSymbol.Mode.Fill));

            freeSymbols.Add(new PolygonSymbol(PolygonSymbol.Mode.Fill, 
                new float[] {0,0, 1,0, 0,1 }));

            freeSymbols.Add(new PolygonSymbol(PolygonSymbol.Mode.Draw,
                new float[] { 0.5F,0, 0.9F, 1F, 0.1F, 1F }));

            freeSymbols.Add(new PolygonSymbol(PolygonSymbol.Mode.Fill,
                new float[] { 0.5F, 0, 0.9F, 1F, 0.1F, 1F }));

            // single lines
            freeSymbols.Add(new LineSymbol(new float[] { 0.5F, 0, 0.5F, 1 }));
            freeSymbols.Add(new LineSymbol(new float[] { 0, 0.5F, 1, 0.5F }));
            freeSymbols.Add(new LineSymbol(new float[] { 1, 0, 0, 1 }));

            // star
            freeSymbols.Add(new LineSymbol(new float[] { 1, 0, 0, 1, 0, 0, 1, 1, 0, 0.5F, 1, 0.5F }));

            // double lines
            freeSymbols.Add(new LineSymbol(new float[] { 0F, 0.4F, 1, 0.4F, 0F, 0.6F, 1, 0.6F }));
            freeSymbols.Add(new LineSymbol(new float[] { 0.9F, 0F, 0F, 0.9F, 1.0F, 0.1F, 0.1F, 1.0F }));

            // tree symbols
            freeSymbols.Add(new LinesSymbol(new float[] { 0.2F, 0.2F, 0.5F, 0.6F, 0.8F, 0.2F }));
            freeSymbols.Add(new LinesSymbol(new float[] { 0.2F, 0.8F, 0.5F, 0.4F, 0.8F, 0.8F }));

            freeSymbols.Add(new LineSymbol(new float[] { 0.2F, 0.2F, 0.5F, 0.6F, 0.5F, 0.6F, 0.8F, 0.2F, 0.2F, 0.5F, 0.5F, 0.9F, 0.5F, 0.9F, 0.8F, 0.5F }));
            freeSymbols.Add(new LineSymbol(new float[] { 0.2F, 0.8F, 0.5F, 0.4F, 0.5F, 0.4F, 0.8F, 0.8F, 0.2F, 0.5F, 0.5F, 0.1F, 0.5F, 0.1F, 0.8F, 0.5F }));

            // T
            freeSymbols.Add(new LineSymbol(new float[] { 0.1F, 0.2F, 0.9F, 0.2F, 0.5F, 0.2F, 0.5F, 0.9F }));
            // L
            freeSymbols.Add(new LineSymbol(new float[] { 0.1F, 0.1F, 0.1F, 0.9F, 0.1F, 0.9F, 0.9F, 0.9F }));
            freeSymbols.Add(new LineSymbol(new float[] { 0.1F, 0.1F, 0.1F, 0.9F, 0.1F, 0.9F, 0.9F, 0.9F, 0.4F, 0.1F, 0.4F, 0.6F, 0.4F, 0.6F, 0.9F, 0.6F }));

            freeSymbols.Add(new LineSymbol(new float[] { 0.9F, 0.1F, 0.1F, 0.9F, 0.1F, 0.9F, 0.9F, 0.9F }));

            // Hourglass
            freeSymbols.Add(new PolygonSymbol(PolygonSymbol.Mode.Draw, new float[] { 0.2F, 0.2F, 0.8F, 0.2F, 0.2F, 0.8F, 0.8F, 0.8F, 0.2F, 0.2F }));

            // Hourglass
            freeSymbols.Add(new PolygonSymbol(PolygonSymbol.Mode.Fill, new float[] { 0.3F, 0.2F, 0.7F, 0.2F, 0.3F, 0.8F, 0.7F, 0.8F, 0.3F, 0.2F }));

        }


        internal void Reset()
        {
            symbols.Clear();
            symbolColors.Clear();
            freeSymbols.Clear();
            CreateSymbols();
        }
    }
}
